package festrung

import (
	"fmt"
	"sync"
	"time"

	"starsine.cn/appTpl/cmn"
	"starsine.cn/appTpl/cmn/logs"
	"starsine.cn/appTpl/cmn/timermgr"
	"starsine.cn/appTpl/svclog/src/fe/openapi"
	"starsine.cn/appTpl/svclog/src/fe/ws-server/wsserver"
	. "starsine.cn/appTpl/svclog/src/logproc/defs"
	"starsine.cn/appTpl/svcregistry"
)

type Conn struct {
	expire time.Time
	conn   *wsserver.WsServer
}

func (p *Conn) IsExpired() bool {
	return !p.expire.After(time.Now())
}

type Strung struct {
	InstanceID string
	size       int
	len        int
	data       []*SvcLogLine

	tmID int64
	tm   *timermgr.TimerMgr
	conn []*Conn

	sync.RWMutex
}

func (p *Strung) String() string {
	out := fmt.Sprintf("InstanceID:%s, size:%d, len:%d, conn:len=%d, ", p.InstanceID, p.size, p.len, len(p.conn))
	for i, v := range p.conn {
		out += fmt.Sprintf("[%d]conn=%p, expire:%s; ", i, v.conn, v.expire.String())
	}
	return out
}

func CreateStrung(size int) *Strung {
	p := new(Strung)

	p.InstanceID = svcregistry.GetInstanceID()
	p.size = size
	p.len = 0
	p.data = make([]*SvcLogLine, p.size)

	p.tmID = -1
	p.tm = cmn.GetTm()
	p.conn = make([]*Conn, 0)

	return p
}

func (p *Strung) AddConn(conn *wsserver.WsServer) {
	for _, c := range p.conn {
		if c.conn == conn {
			return
		}
	}

	p.conn = append(p.conn, &Conn{
		conn:   conn,
		expire: time.Now().Add(time.Minute),
	})
}

func (p *Strung) DelConn(conn *wsserver.WsServer) {
	for i, c := range p.conn {
		if c.conn == conn {
			p.conn = append(p.conn[:i], p.conn[i+1:]...)
			return
		}
	}
}

func (p *Strung) Start() {
	p.checkPeriod()
}

func (p *Strung) End() {
	p.tm.CancelTimer(p.tmID)
}

func (p *Strung) checkAndFlush(params interface{}) {
	p.Flush()
}

func (p *Strung) checkPeriod() {
	p.tmID = p.tm.AddPeriodTimer(
		time.Duration(3),
		timermgr.NewOnTimeOut(p.checkAndFlush,
			nil,
		))
}

func (p *Strung) Flush() {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if p.len <= 0 {
		return
	}

	// to flush
	for _, c := range p.conn {
		err := c.conn.SendMessage(WsImmeLogOut{
			MsgHead: openapi.WsHead{
				InstanceID:  p.InstanceID,
				RequestType: "imme-log-out",
				ResOrReq:    "req",
				SerialNum:   0,
			},
			Data: p.data[:p.len],
		})
		if err == nil {
			c.expire = time.Now().Add(time.Minute)
		} else {
			logs.Debugf("c.conn.SendMessage fail, err: %s", err)
			if c.conn.IsConnNil() {
				p.DelConn(c.conn)
			}
		}
	}
	p.len = 0

	if p.tmID != -1 {
		p.tm.CancelTimer(p.tmID)
	}
	p.checkPeriod()
}

func (p *Strung) Join(d *SvcLogLine) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	p.data[p.len] = d
	p.len++

	if p.len >= p.size {
		p.Flush()
	}
}
